"""
------------utf-8--------------
@Author: rfdsg
@Create Time: 2023/6/27 -  16:53
@Description: process the raw factor
"""
import numpy as np
from util.data_process import average_calculated, time_change_deal_calender
import pandas as pd


class FactorProcess:
    """
    Attention:
    =====================================
    注意，接下来，你将只能传入pandas
    =====================================
    """

    def __init__(self, factor: pd.Series, factor_name:str=None):
        self.factor = factor
        if factor_name is None:
            self.factor_name = self.factor.name
        else:
            self.factor_name = factor_name
        self.factor.index = pd.to_datetime(self.factor.index)
        self.factor = self.factor.sort_index()
        self.time = self.factor.index.to_series(name='time')


    def time_roll_concat(self, gap: int = 25):
        if gap < 0:
            raise ValueError('大于等于0')
        else:
            assert isinstance(self.factor, pd.DataFrame) or isinstance(self.factor, pd.Series)
            self.factor.index = self.factor.index + pd.Timedelta(days=gap)
            self.factor.index = time_change_deal_calender(self.factor.index)
            # 进行时间重复的去除
            unique_index = self.factor.index.duplicated(keep='last')
            self.factor = self.factor.loc[~unique_index]
            assert isinstance(self.factor.index, pd.DatetimeIndex)
            trading_calendar = pd.read_pickle(
                r"D:\PycharmProject\quant\多因子数据\0\code_calender\xinglang_sh_stock_19901219_inf_1d.pkl").loc[
                               self.factor.index.min():, :]
            trading_calendar.drop('trade_date', inplace=True, axis=1)
            self.factor = pd.concat([trading_calendar, self.factor], axis=1)
            self.time = self.factor.index.to_series(name='time')

    def fill(self):
        """

        Returns:
            None

        Information
        -----------------------
        先用前一个值填充后面，
        再用后一个值填充前面
        """
        self.factor.fillna(method='pad', inplace=True)
        self.factor.fillna(method='bfill', inplace=True)


    def subtraction_division(self, chsn_diff: bool = False):
        """
        Args:
            chsn_diff:
                True 代表除，False 代表减

        Returns:
            None

    公式
    ------------------------
        factor_n = (factor_n / factor_n-1) (/) factor_n-1

        """
        # np.roll向下或向右滚n个
        if chsn_diff:
            self.factor = (self.factor - np.roll(self.factor, 1, axis=0)) / np.abs(np.roll(self.factor, 1, axis=0))
        else:
            self.factor = self.factor - np.roll(self.factor, 1, axis=0)
        if isinstance(self.factor, pd.DataFrame):
            self.factor.iloc[0, :] = np.nan
        elif isinstance(self.factor, pd.Series):
            self.factor[0] = np.nan
        elif isinstance(self.factor, np.ndarray):
            self.factor[0, :] = np.nan
        else:
            raise ValueError('类型错误')

    def smooth(self, edge_value: float):
        """

        Args:
            edge_value:
                边际值，factor最极限值

        Returns:
            None

        Information
        -----------------------
        max_fct和min_fct公式：
            6(((FCT-FCT中位数)中位数).abs)+FCT中位数
        """
        factorl_quantile = np.nanquantile(self.factor, axis=0, q=0.5)
        delta = np.abs(self.factor * 1.0 - factorl_quantile)
        # 要有六位的float型
        max_fct = factorl_quantile + edge_value * np.nanquantile(delta, axis=0, q=0.5) * 1
        min_fct = factorl_quantile - edge_value * np.nanquantile(delta, axis=0, q=0.5) * 1
        self.factor = np.clip(self.factor, min_fct, max_fct)

    def average_fct(self):
        average = self.factor.loc[:, ['open', 'close', 'high', 'low']]
        average = average.mean(axis=1)
        # print('这是平均值')
        # print(average)
        # print(type(average.iloc[0]))
        # 因子数据
        fct = pd.DataFrame()
        fct['one_average'] = average_calculated(average, average, 1)
        fct['close_average'] = average_calculated(average, self.factor.close, 0)
        fct['two_average'] = average_calculated(average, average, 2)
        fct['color'] = self.factor.close - self.factor.open
        # print('这是fct值')
        # print(fct)
        weight = pd.Series([0.6, 0.3, 0.3, 0.1])
        fct = np.dot(fct, weight)
        fct = 10000 * fct / average.iloc[-1]
        self.factor = pd.Series(fct, index=self.time)

    def rsi(self, window=14):
        """
        计算相对强弱指标（Relative Strength Index，RSI）。
        有return值，self.factor不变

        参数：
        - self.close: 价格数据，可以是单一股票价格的时间序列。
        - window: 计算RSI的窗口期。

        返回：
        - rsi: 计算得到的RSI序列。
        """
        # 计算价格变化
        delta = self.factor.diff()

        # 计算上涨和下跌的价格变化
        gain = delta.where(delta > 0, 0)
        loss = -delta.where(delta < 0, 0)

        # 使用指数移动平均计算平均上涨和平均下跌
        avg_gain = gain.ewm(span=window, adjust=False).mean()
        avg_loss = loss.ewm(span=window, adjust=False).mean()

        # 计算相对强弱指数（RS）
        rs = avg_gain / avg_loss

        # 计算RSI
        rsi = 100 - (100 / (1 + rs))
        rsi.iloc[0, :] = 0

        return rsi

    def decay(self, method: str):
        """

        Args:
            method:
                默认: "EXP"

        Returns:
            None
        Information
        -------------------
            先选出每列的连续相同值，并分成若干组，每组中每个值变为对应的值出现的次数。
            并用不同method进行衰减
        """
        self.factor =self.factor.values
        if method != 'EXP':
            pass
        # ACT(T) =  ACT(RAW)* np.EXP( - ALPHA * T)
        elif method == 'EXP':
            t_ondecay = np.zeros_like(self.factor)
            for i in range(self.factor.shape[1]):
                for n in range(self.factor.shape[0]):
                    # 计算 t_ondecay 的条件
                    if self.factor[n, i] == self.factor[n - 1, i]:
                        t_ondecay[n, i] = t_ondecay[n - 1, i] + 1
                    else:
                        t_ondecay[n, i] = 0
                alpha = 0.2
                self.factor[:, i] *= np.exp(-alpha * t_ondecay[:, i])
        self.factor = pd.Series(self.factor, name=self.factor_name)
